package robocup.component.geometry;

import java.util.LinkedList;

import robocup.component.geometry.analysers.ReachableArea;

/**
 * A circle represented by its center and its radius.
 */
public class Circle {

  /**
   * This circle's center.
   */
  private final Vektor        _center   = new Vektor();

  /**
   * This circle's radius.
   */
  private double              _radius;

  /** ************************** DUMMIES ************************* */

  private final StraightLine  dummyLine = StraightLine.getDummyInstance();
  
  /** ************************** STATICS ************************* */

  /**
   * "epsilon"
   */
  private final static double E         = 0.000001;

  /** ************************** CONSTRUCTORS ******************** */

  /**
   * Creates a new circle.
   * 
   * <br>
   * NEW OBJECTS: 3 + this
   * 
   * @param center
   *          The center.
   * @param radius
   *          The radius.
   */
  public Circle(
      final Vektor center,
      final double radius) {

    this._center.copy(center);
    this._radius = radius;
  }

  /** ************************** STATICS ************************* */

  /**
   * <br>
   * NEW OBJECTS = 4
   * 
   * @return a new Circle
   */
  public final static Circle getDummyInstance() {

    return new Circle(Vektor.CENTER, 1);
  }

  /** ************************** METHODS ************************* */

  /**
   * Copies the values of line into this object
   * 
   * @param c
   *          the Circle to copy
   */
  public void copy(final Circle c) {

    _center.copy(c.getCenter());
    _radius = c._radius;
  }

  /**
   * Creates a duplicate of this Circle
   * 
   * <br>
   * NEW OBJECTS: 4
   * 
   * @return a duplicate of this object
   */
  public final Circle cloned() {

    return new Circle(_center, _radius);
  }

  /**
   * Calculates the intersection points to a StraightLine.
   * 
   * <br>
   * NEW OBJECTS <= 3
   * 
   * <br>
   * USES StraightLine.dummyVektor
   * 
   * @param line
   *          the StraightLine to intersect with
   * @return a LinkedList containing all intersection points
   */
  public final LinkedList<Vektor> getIntersectionPoints(final StraightLine line) {

    return line.getIntersectionPoints(this);
  }

  /**
   * Calculates the intersection points to a LineSegment.
   * 
   * <br>
   * NEW OBJECTS <= 3
   * 
   * <br>
   * USES LineSegment.dummyVektor
   * 
   * @param line
   *          the LineSegment to intersect with
   * @return a LinkedList containing all intersection points
   */
  public final LinkedList<Vektor> getIntersectionPoints(final LineSegment line) {

    return line.getIntersectionPoints(this);
  }

  /**
   * Calculates the intersection points to a StraightLine.
   * 
   * <br>
   * NEW OBJECTS <= 2
   * 
   * <br>
   * USES StraightLine.dummyVektor
   * 
   * @param line
   *          the StraightLine to intersect with
   * @param result
   *          old contents will be overwritten. Here the results are saved.
   */
  public final void getIntersectionPoints(final StraightLine line,
      final LinkedList<Vektor> result) {

    line.getIntersectionPoints(this, result);
  }

  /**
   * Calculates the intersection points to a LineSegment.
   * 
   * <br>
   * NEW OBJECTS <= 2
   * 
   * <br>
   * USES LineSegment.dummyVektor
   * 
   * @param line
   *          the LineSegment to intersect with
   * @param result
   *          old contents will be overwritten. Here the results are saved.
   */
  public final void getIntersectionPoints(final LineSegment line,
      final LinkedList<Vektor> result) {

    line.getIntersectionPoints(this, result);
  }

  /**
   * <br>
   * USES --NO DUMMIES--
   * 
   * @param point
   *          The point.
   * @return <code>true</code>, if the given point lies within or on the
   *         circle, <code>false</code> otherwise.
   */
  public final boolean liesWithinCircle(final Vektor point) {

    return point.getDistance(this._center) <= this._radius;
  }

  /**
   * Calculates the intersection points between this Circle and a LineSegment and adds them to the list.
   * 
   * <br>
   * NEW OBJECTS <= 3
   * <br>
   * USES LineSegment.dummyVektor
   * 
   * @param line
   *          the LineSegment to intersect with
   * @param list
   *          here the result will be added
   */
  public void addIntersectionPointsToList(final LineSegment line,
      final LinkedList<Vektor> list) {
    line.addIntersectionPointsToList(this, list);
  }

  /**
   * Calculates the intersection points between this Circle and a CircleSegment and adds them to the list.
   * 
   * <br>
   * NEW OBJECTS <= 3
   * <br>
   * USES CircleSegment.dummyLine
   * 
   * @param circ
   *          the CircleSegment to intersect with
   * @param list
   *          here the result will be added
   */
  public void addIntersectionPointsToList(final CircleSegment circ,
      final LinkedList<Vektor> list) {
    circ.addIntersectionPointsToList(this, list);
  }

  /**
   * Calculates the intersection points between two Circles and adds them to the list.
   * 
   * <br>
   * NEW OBJECTS <= 3
   * <br>
   * USES Circle.dummyLine
   * 
   * @param circ
   *          the Circle to intersect with
   * @param list
   *          here the result will be added
   */
  public void addIntersectionPointsToList(final Circle circ,
      final LinkedList<Vektor> list) {
    LinkedList<Vektor> list2= getIntersectionPoints(circ);
    for (Vektor v: list2) {
      if (v != null) {
        list.add(v);
      }
    }
  }

  /**
   * Calculates the intersection points between this Circle and a StraightLine and adds them to the list.
   * 
   * <br>
   * NEW OBJECTS <= 3
   * 
   * <br>
   * USES StraightLine.dummyVektor
   * 
   * @param line
   *          the StraightLine to intersect with
   * @param list
   *          here the result will be added
   */
  public void addIntersectionPointsToList(final StraightLine line,
      final LinkedList<Vektor> list) {
    LinkedList<Vektor> list2= getIntersectionPoints(line);
    for (Vektor v: list2) {
      if (v != null) {
        list.add(v);
      }
    }
  }

  /**
   * Calculates the intersection points between both Circles
   * 
   * <br>
   * NEW OBJECTS <= 2
   * <br>
   * USES Circle.dummyLine
   * 
   * @param c
   *          the Circle to intersect with
   * @param result
   *          old contents will be overwritten. Here the results are saved.
   */
  public final void getIntersectionPoints(final Circle c,
      final LinkedList<Vektor> result) {

    if (!result.isEmpty()) {
      result.clear();
    }
    final double dist = c.getCenter().getDistance(getCenter());
    if (dist > c.getRadius() + getRadius()) {
      return;
    }
    if (Math.max(getRadius(), c.getRadius()) - dist > Math.min(c.getRadius(),
        getRadius())) {
      return;
    }

    final double x1 = getCenter().x;
    final double x2 = c.getCenter().x;
    final double y1 = getCenter().y;
    final double y2 = c.getCenter().y;
    double dividor = y2 - y1;
    if (dividor == 0) {
      dividor += Circle.E;
    }

    final double m = (x1 - x2) / dividor;
    final double n = (x2
        * x2
        - x1
        * x1
        + y2
        * y2
        - y1
        * y1
        + getRadius()
        * getRadius() - c.getRadius() * c.getRadius())
        / (2 * dividor);
    dummyLine.setStraightLine(m, n);
    // System.out.println("int line: "+dummyLine);
    dummyLine.getIntersectionPoints(this, result);
  }

  /**
   * Calculates the intersection points between both Circles
   * 
   * <br>
   * NEW OBJECTS <= 3
   * <br>
   * USES Circle.dummyLine
   * 
   * @param c
   *          the Circle to intersect with
   * @return a list of intersection points
   */
  public final LinkedList<Vektor> getIntersectionPoints(final Circle c) {

    LinkedList<Vektor> res = new LinkedList<Vektor>();
    getIntersectionPoints(c, res);
    return res;
  }

  /**
   * Calculates the intersection points between both Circles
   * 
   * <br>
   * NEW OBJECTS <= 2
   * <br>
   * USES CircleSegment.dummyLine
   * 
   * @param c
   *          the Circle to intersect with
   * @param result
   *          old contents will be overwritten. Here the results are saved.
   */
  public final void getIntersectionPoints(final CircleSegment c,
      final LinkedList<Vektor> result) {

    c.getIntersectionPoints(this, result);
  }

  /**
   * Calculates the intersection points between both Circles
   * 
   * <br>
   * NEW OBJECTS <= 3
   * <br>
   * USES CircleSegment.dummyLine
   * 
   * @param c
   *          the Circle to intersect with
   * @return a list of intersection points
   */
  public final LinkedList<Vektor> getIntersectionPoints(final CircleSegment c) {

    return c.getIntersectionPoints(this);
  }
  
  /**
   * Calculates the intersection points of this Circle and a RA and add the results to the given list.
   * 
   * @param ra the RA to intersect with
   * @param back if true, backwards movement is allowed
   * @return a list that contains the intersection points
   */
  public final LinkedList<Vektor> getIntersectionPoints(final ReachableArea ra, final boolean back) {
    return ra.getIntersectionPoints(this, back);
  }
  
  /**
   * Calculates the intersection points of this Circle and a RA and add the results to the given list.
   * 
   * @param ra the RA to intersect with
   * @param back if true, backwards movement is allowed
   * @param list here the results are added
   */
  public final void addIntersectionPointsToList(final ReachableArea ra, final boolean back, final LinkedList<Vektor> list) {
    ra.addIntersectionPointsToList(this, back, list);
  }

  /**
   * 
   * @return the _center.
   */
  public final Vektor getCenter() {

    return this._center;
  }

  /**
   * @param center
   *          The center to set.
   */
  public void setCenter(final Vektor center) {

    this._center.copy(center);
  }

  /**
   * @return Returns the radius.
   */
  public final double getRadius() {

    return this._radius;
  }

  /**
   * @param radius
   *          The radius to set.
   */
  public void setRadius(final double radius) {

    this._radius = radius;
  }

  /**
   * @return Returns the circle's diameter.
   */
  public final double getDiameter() {

    return this._radius * 2;
  }

  /**
   * @return Returns the circle's area.
   */
  public final double getArea() {

    return Math.PI * Math.pow(this._radius, 2);
  }

  /**
   * @return Returns the circle's circumference.
   */
  public final double getCircumference() {

    return 2 * Math.PI * this._radius;
  }

  /**
   * (non-Javadoc)
   * 
   * @see java.lang.Object#toString()
   */
  @Override
  public final String toString() {

    return "center: " + this._center + " rad: " + this._radius;
  }

}
